//=============================================================================
///	S-expression
/**

different:
	* error for pass nil to car or cdr

*/
//=============================================================================

#ifndef	__SEXP_H__
#define	__SEXP_H__

#include "config.h"
#include "stype.h"
#include <stdio.h>		// FILE
#include <assert.h>

#ifdef __cplusplus
extern "C" {
#endif

extern SExp nil;
extern SExp t;

//=============================================================================
// Function definition

/// Init context
void sexp_new(void);

/// Term context
void sexp_delete(void);

/// Set error callback
void sexp_set_error_callback(SExp (*cb)(const char*, ...));

// Lisp like function
SExp cons(SExp a, SExp d);
SExp car(SExp s);
SExp cdr(SExp s);
SType type_of(SExp);
const STypeInfo* get_type_info(SExp s);



void rplaca(SExp s, SExp a);		///< replace car part
void rplacd(SExp s, SExp d);		///< replace cdr part

extern SExp (*error)(const char* msg, ...);
SExp type_check(SExp s, SType type);

SExp gen_specialform(SpecialFormType cfunc, const char* name, int minargnum, int maxargnum);
SExp gen_subr(SubrType cfunc, const char* name, int minargnum, int maxargnum);
SExp gen_lambda(SExp arglist, SExp body, SExp lexical_env);
SExp gen_macro(SExp arglist, SExp body, SExp lexical_env);
void set_name(SExp s, SExp sym);

// for read cell
SExp cons_ext(SExp a, SExp d, SExp filename, int lineno, int colno);
int cons_extp(SExp s);

//=============================================================================
// Inline functions

/// fixnum?
INLINE int fixnump(SExp s)		{ return s.i & 1; }

/// fixnum -> S exp
INLINE SExp fixnum2s(SFixnum x)	{ SExp s; s.i = (x << 1) | 1; return s; }

/// S exp -> fixnum
INLINE SFixnum s2fixnum(SExp s)	{ assert(fixnump(s)); return s.i >> 1; }

/// char -> S exp
INLINE SExp char2s(int c)		{ return MAKE_SPECIAL(SUBTYPE_CHAR, c); }

/// S exp -> char
INLINE int s2char(SExp s)		{ return GET_SUBVALUE(s.i); }

/// eq?
INLINE int eq(SExp a, SExp b)	{ return a.ptr == b.ptr; }

/// cons?
INLINE int consp(SExp s)		{ return type_of(s) == tCell; }

/// nil?
INLINE int nilp(SExp s)			{ return eq(s, nil); }

/// symbol?
INLINE int symbolp(SExp s)		{ return type_of(s) == tSymbol; }

/// pointer -> S exp
INLINE SExp ptr2s(const void* p){ SExp s; s.ptr = (union SExpPtr*)p; return s; }

/// pointer?
INLINE int sptrp(SExp s)		{ return (((int)s.ptr) & 3) == 0; }

/// cxxr
INLINE SExp cadr(SExp s)		{ return car(cdr(s)); }
INLINE SExp cddr(SExp s)		{ return cdr(cdr(s)); }
INLINE SExp caddr(SExp s)		{ return car(cddr(s)); }
INLINE SExp cdddr(SExp s)		{ return cdr(cddr(s)); }
INLINE SExp cadddr(SExp s)		{ return car(cdddr(s)); }
INLINE SExp cddddr(SExp s)		{ return cdr(cdddr(s)); }



#ifdef __cplusplus
} // extern "C"
#endif

#endif
